Sync fork with upstream v0.3.0 (37 commits) - #159
Closed
joesteinkamp wants to merge 46 commits into
Closed
Conversation
Introduce two object-shaped color values that the model expands into the
existing flat colors map, plus three rules that enforce the new structure.
Flat hex colors keep working unchanged.
Schema (in YAML, additive):
primary: # ramp
type: ramp
anchor: "#1A1C1E"
humanName: "Boston Clay"
pairs: { container: { bg: 100, fg: 800 } }
surface-info: # standalone pair
type: pair
container: "#E0F2FE"
onContainer: "#0C4A6E"
Ramps interpolate 50/100/.../900 in OKLCH (pure-TS, no new dep) preserving
chroma and hue and varying L from anchor toward white/black. Steps land in
state.colors at `<ramp>.<step>` and resolve via `{colors.<ramp>.<step>}`;
the bare ramp name still resolves to the anchor.
Pairs land in state.colors as both dotted members
(`<pair>.container`/`<pair>.onContainer`) and hyphenated flat aliases
(`<pair>` = container, `on-<pair>` = on-container). Inline-ramp pairs
synthesize M3-style flat aliases (`primary-container`,
`on-primary-container`) for back-compat with existing systems.
New rules:
- pair-contrast (error): every declared pair meets its minContrast floor
- mixed-pair-foreground (warning): a pair container backgroundColor must
be paired with the matching on-container as textColor
- ramp-anchor-naming (warning): every ramp should declare humanName so
prose-token-mismatch (#4) can validate "Boston Clay (#1A1C1E)" sentences
Updated rules:
- orphaned-tokens: exempts individual ramp steps and pair members so a
single declaration doesn't flood the report; only the anchor / pair
surfaces a warning when nothing references the group
Exporters:
- Tailwind: ramps emit as nested `{ DEFAULT, '50', ..., '900' }` objects;
pair members emit as hyphenated flat keys
- DTCG: ramps emit as nested groups with $extensions['design.md'] vendor
metadata and an anchor alias; standalone pairs emit as nested groups
with role $extensions on each member
Tests: +34 (203 → 237 passing). Adds OKLCH conversion tests, model
expansion tests, three new rule tests, exporter tests for the new shapes,
and an end-to-end fixture (RAMPS_AND_PAIRS.md).
Documents the new color shapes via comments in spec-config.yaml. Spec
doc regeneration and example migration are deferred follow-ups (each
requires renderer support and fixture-test churn that warrants its
own PR).
https://claude.ai/code/session_01E3CsAihbbFG354mb8DNuGG
Promotes border, borderColor, borderWidth, shadow, elevation, gap, iconSize, opacity, and transition from "accept with warning" to typed component sub- tokens with per-property validators. Adds an `elevation` top-level token group for semantic shadows (resting / raised / overlay / modal) and four new lint rules — opacity-stacking, animating-layout-property, elevation-without- semantics, triple-separation — that enforce the prose guidelines from the Components section. Tailwind exporter now projects elevation tokens to `theme.extend.boxShadow`; the DTCG exporter emits an `elevation` shadow group. Examples (atmospheric- glass, paws-and-paths, totality-festival) are migrated to the new vocabulary and define elevation tokens. https://claude.ai/code/session_01DLsGxzVXmTAwKWY4JZNn2U
…3HngO feat(colors): ramps and pairs as schema primitives (#1)
…e-2-plan-4Oexv # Conflicts: # packages/cli/src/commands/spec.test.ts # packages/cli/src/linter/linter/rules/index.ts # packages/cli/src/linter/linter/rules/types.test.ts # packages/cli/src/linter/model/handler.ts # packages/cli/src/linter/tailwind/handler.test.ts # packages/cli/src/linter/tailwind/handler.ts
Introduces a nested `states:` block under each component so transient responses (hover, focus-visible, active, pressed, disabled, loading) are modeled as inheriting overrides on a base — replacing the brittle `*-hover` flat-sibling convention. Implements the design plan from #3. - Schema: `component_states` (vocabulary) + per-component `interactive` flag and nested `states` map. Adds new sub-tokens (opacity, outline, boxShadow, border, cursor) so state overrides can express common affordance changes. - Model: `ComponentDef` gains `states`, `resolvedStates` (base ⊕ overrides pre-merged so downstream consumers do not reimplement merging), and `interactive`. - New linter rules: `unchanged-state` (warn), `missing-focus-visible` (error), `outline-none-without-replacement` (error), `hover-only-affordance` (warn), `disabled-opacity-only` (warn), `unknown-state` (warn). - Updated rules: `broken-ref`, `contrast-ratio`, `orphaned-tokens` now recurse into per-state overrides. - Exporters: tailwind opt-in `--components` flag emits a plugin object with `&:hover`, `&:focus-visible`, `&:active`, `&:disabled` nested rules suitable for `addComponents()`. DTCG emitter projects components into a `component` group with `$extensions['design.md'].states`. - Examples migrated from `*-hover` siblings to nested `states.hover` with focus-visible and disabled added (the latter would otherwise surface real `missing-focus-visible` errors). - Spec doc regenerated with a new "Interactive States" subsection covering state hierarchy, focus-visible discipline, disabled affordance, hover-is-desktop-only, state-vs-variant, and loading. Closes #3. https://claude.ai/code/session_018VrVVrSx1PAEnrtNwywhjq
…n-4Oexv feat(linter): expand component property vocabulary (#2)
…ppressions Detects drift between hex literals embedded in markdown prose and the resolved values of color tokens. Two sub-rules under one rule name: - orphan: any hex in prose must appear as some token's value - anchored: when a hex is anchored to a backticked token key or a ramp anchor's humanName, the hex must equal that token's resolved value Adds a generic comment-directive suppression mechanism (disable-next-line / disable+enable / disable-file) parsed by the markdown parser and consulted per-finding. Sections now carry startLine/endLine and fenced code-block ranges so prose-aware rules can target source positions and skip illustrative blocks. Closes #4
feat(linter): add prose-token-mismatch rule with suppression directives
Merges PRs #10 (color ramps and pairs) and #11 (typed property vocabulary, elevation tokens) from main into the states branch. Key reconciliation points: - spec-config.yaml: merged sub-token vocabulary so the combined set covers border/borderColor/shadow/elevation/gap/iconSize/opacity/transition (from #11) plus outline/boxShadow/cursor (needed for state overrides). - model/handler.ts: combined the typed-validator + elevation-shorthand resolution from main with the per-state override merging from the states branch. `interactive` and `states` are skipped in the property loop and validators run per state-override too. - DTCG exporter: emits both the elevation group (main) and the component group with `$extensions['design.md'].states` (this branch). - Tailwind exporter test: ramps/pairs/elevation tests + components plugin test all live alongside. - Example design files: merged main's typed properties (transition, shadow, border, iconSize) onto each component's base, with the state branch's `interactive: true` + `states:` block layered on top. `transparent` literal replaced with `#00000000` to satisfy the typed Color validator. All 328 tests pass; spec.md regenerated; design_tokens.json regenerated. https://claude.ai/code/session_018VrVVrSx1PAEnrtNwywhjq
Resolves three rule-count conflicts so both main's prose-token-mismatch rule and the PR's six new state-related rules land together (total 22): - packages/cli/src/linter/linter/rules/index.ts - packages/cli/src/linter/linter/rules/types.test.ts - packages/cli/src/commands/spec.test.ts bun test: 349/349 pass. https://claude.ai/code/session_018VrVVrSx1PAEnrtNwywhjq
…3UH7 Resolve merge conflicts for PR #12 (component states)
Closes the open-world component schema with an opt-in registry. When the
`components:` block uses the `registry: + definitions:` shape, five new
rules engage:
- `unbound-component` (error) — flags definitions and `{components.X}`
prose references whose name isn't in the registry.
- `missing-required-property` (error) — `requiredProperties` on a
registry entry must be set by the matching definition; composed
properties count.
- `registry-without-definition` (warning) — registry entries that lack
a matching definition.
- `composes-cycle` (error) — cycles in the `composes:` graph.
- `naming-convention` (warning) — registry names follow `noun-modifier`
with a closed modifier vocabulary.
The flat `components:` shape continues to work unchanged; the registry
is fully opt-in. Adds `kind` (button/input/container/display/navigation/
overlay) which drives default interactivity, and `composes:` for
property pre-merge before child overrides. Documents the registry
authoring discipline in the Components section of the spec.
Defers source-side scanning (`--src` flag), exporters changes
(Tailwind/DTCG), and the bulk migration of existing example files —
these are independent rollout phases listed in the design doc.
Resolved conflicts so the new registry feature (#6) coexists cleanly with the recently-merged first-class component states feature (#14). Both streams are largely orthogonal — registry is the closed-world set of component names, states are per-component variants — and combine in the natural way: - spec-config.ts now exports both COMPONENT_KINDS/COMPONENT_MODIFIERS (registry) and COMPONENT_STATES (states); the SpecConfig surface bundles both. - parser/spec.ts: keep RawRegistryEntry alongside RawComponentValue so definitions can carry the nested states block while still being validated against the registry. - parser/handler.ts: normalizeComponents now produces the RawComponentValue-shaped definitions map, preserving registry detection. - DEFAULT_RULE_DESCRIPTORS aggregates 16 baseline + 6 state + 5 registry = 27 rules. - spec.mdx: registry section ("Component Registry (Closed-World)") precedes the new "Interactive States" section in the Components documentation. - docs/spec.md regenerated from the merged spec.mdx. All 383 tests pass.
…n-hmt8L feat(linter): component registry + closed-world rules (#6)
Closes #5. Two new top-level token groups land in the spec: - motion: duration (ms/s), easing (CSS keywords + cubic-bezier with control-point extraction), and a required reducedMotion fallback. Embedded refs in component transition shorthands resolve inline so exporters see literal values. - iconography: a single library reference (closed enum: lucide, material-symbols, heroicons, phosphor, custom-svg), stroke weight, size scale, default size, and color-binding rule. Pipeline changes: - spec-config.yaml grows the two groups + Motion / Iconography canonical sections (inserted between Layout/Elevation and Shapes/Components). - ParsedDesignSystem and DesignSystemState extend with the new shapes; ResolvedDuration and ResolvedEasing join the ResolvedValue union. - ModelHandler gains parseMotion / parseIconography plus an embedded-ref resolver invoked on component shorthand strings. - Tailwind exporter emits transitionDuration and transitionTimingFunction. - DTCG exporter emits motion.duration as duration-typed tokens, easings as cubicBezier (4-tuple for parsed beziers, string + extension for keywords), reducedMotion under the design.md vendor extension, and iconography.sizes as a dimension group with the metadata under $extensions['design.md'].iconography. New lint rules: - missing-reduced-motion (warning) — motion declared without a reducedMotion fallback. - overlong-duration (warning) — anything over 400ms reads as broken. - icon-size-off-scale (warning) — component iconSize literals that don't match an iconography.sizes entry. Existing rules adapt: token-summary counts motion + icon-size tokens; orphaned-tokens uses ComponentDef.referencedTokens (recorded pre-resolution) to avoid false positives now that embedded refs get substituted; broken-ref already covers unresolved embedded refs via the existing unresolvedRefs channel. Examples: paws-and-paths gains Motion + Iconography sections wired to real component transitions and iconSize references; tailwind.config.js and design_tokens.json regenerated. https://claude.ai/code/session_01Poxc1vkHRW2UdBQzS1gv2h
Brings the motion + iconography work in alignment with the new component states / closed-world registry features that landed on main. Conflict resolutions: - spec-config.yaml/.ts: union of icon_libraries + easing_keywords (mine) and component_kinds + component_modifiers + ComponentExample schema (theirs); examples.button-primary keeps my motion transition AND theirs interactive/states block. - parser/spec.ts + handler.ts: union of RawMotionDef/RawIconographyDef (mine) and RawRegistryEntry/RawComponentValue + componentRegistry (theirs). - model/handler.ts: imports unioned; Phase 3 component loop combines mine (referencedTokens tracker) with theirs (interactive flag, states map, resolveComponentValue helper). Embedded-ref resolution for transition shorthands moved into resolveComponentValue's fallback branch so it composes with theirs. - linter/rules/index.ts + tests: rule descriptors unioned (count becomes 30: theirs' 27 plus mine 3); test asserts updated. - dtcg/handler.ts: union of mapMotion/mapIconography (mine) and mapComponents (theirs). - examples/paws-and-paths/DESIGN.md: each conflicting component keeps the motion+iconography refs from mine AND the interactive/states blocks from theirs. design_tokens.json + tailwind.config.js regenerated. - docs/spec.md: regenerated from the unioned spec.mdx + spec-config. Tests pass (408), build is clean. https://claude.ai/code/session_01Poxc1vkHRW2UdBQzS1gv2h
…n-Kngwg Add motion and iconography token support to design system
…r rules (#8) Cover the verbal half of brand identity. The eight prescribed sections are entirely visual; two products with identical DESIGN.md files and different brands sounded identical until now. This change adds machine-readable voice + copy primitives, a Voice prose section between Typography and Layout, and six new linter rules that enforce them. Schema: - `voice:` — formality / warmth / authority / playfulness axes (1–5), plus person, tense, oxfordComma, contractions. - `copy:` — per-surface casing, buttonLabelMaxWords, errorPattern, emptyStateTone, bannedTerms, bannedRegex, approvedTerms, reservedNames, titleCase overrides. Model: - `parseVoice` / `parseCopy` validate axis ranges, casing enums, and pre-compile bannedRegex once per build. - Voice/copy keys are surfaced in the symbol table so `{voice.warmth}` style references resolve through the existing broken-ref machinery. Linter rules: - banned-term-in-prose — flags banned terms / regexes in prose and component label-bearing properties (label, placeholder, title, aria-label). Whole-word literal match; phrase match for terms with spaces. - button-exceeds-word-limit — flags button labels over the configured cap. Soft-depends on the registry's `kind: button`; falls back to a name-prefix heuristic. - casing-mismatch — verifies labels match copy.casing.<surface> (button / nav / section-heading). Sentence-case, title-case, UPPERCASE, lowercase. knownProperNouns + titleCase.exceptions control false positives. - approved-term-violation — flags `user` where `customer` is required. - reserved-name-form — flags lowercased / hyphenated variants of reserved product names. - error-pattern-violation — checks error-message components against copy.errorPattern's slot count + literal separators. Sections + outputs: - CANONICAL_ORDER inserts Voice between Typography and Layout, with `Tone` and `Voice & Tone` aliases. - token-summary now reports the banned-term count. - DTCG exporter emits voice + copy under `$extensions['design.md']` so downstream tools consume without losing fidelity. - spec.mdx has a Voice section narrative + axes/casing tables; docs/spec.md regenerated. Examples: - atmospheric-glass: formal, cold, technical voice (formality 4, warmth 2, third person, contractions avoided). - paws-and-paths: warm consumer voice (formality 2, warmth 5, second person, contractions permitted), with `user → pet parent` approved term and an encouraging empty-state tone. - totality-festival: cinematic / dramatic (playfulness 4, UPPERCASE buttons + nav). Other: - Frontmatter is now part of the parser's code-block ranges so prose-aware rules don't scan YAML. - New VOICE.md fixture covers end-to-end parsing + rule firing. - 6 new rule unit tests + voice/copy model tests in handler.test.ts. https://claude.ai/code/session_01Jig9jMBKFgijyTtk1VdFEo
…sity) Implements the foundation laid out in #7's run-plan. Themes declare overrides on top of the implicit `light` base; resolution deep-merges them per-theme so the same token name can carry different values across modes. Two new parity rules surface the common failure modes; the existing contrast-ratio rule now runs per theme against per-theme contrast targets; Tailwind output gains a `themes` field that mirrors the base shape per theme. Schema (spec-config.yaml): - New `Themes` canonical section (alias: `Modes`, `Themes & Modes`). - `well_known_themes` informational list (light, dark, high-contrast, comfortable, compact). The list is open — authors may declare other theme names. Parser: - `RawThemeDef` carries `inheritsFrom`, color/typography/rounded/spacing/ elevation overrides, and a `contrastTarget`. - `ParsedDesignSystem.themes` passes through unchanged YAML. Model: - `ThemeView` carries a fully resolved per-theme view (colors, ramps, pairs, typography, spacing, rounded, elevation, contrastTarget, explicitColorOverrides). The implicit `light` base is always present. - `DesignSystemState.themes` and `activeTheme` join the existing fields. Top-level fields still mirror the active theme so existing rules keep working unchanged. - Theme resolution honors `inheritsFrom` chains, breaks cycles with a warning, and re-runs ramp / pair expansion within a per-theme symbol table so `{colors.primary}` resolves theme-locally. - ComponentDef gains `propertyRefs` and `stateRefs` so per-theme rules can re-resolve color references through any theme view. Rules: - `theme-parity` (warning) — every base color must be explicitly overridden in each declared theme. - `pair-parity` (warning) — half-overriding a color pair across themes silently breaks the contrast contract. - `contrast-ratio` runs per theme against the theme's `contrastTarget` (defaults to WCAG AA; high-contrast themes typically raise body to 7). Tailwind exporter: - `mapColors` becomes view-driven; per-theme overrides emit under a new `themes` field on the result. Authors who don't declare themes see no output change. Examples: - `examples/paws-and-paths` gains a `themes.dark` palette with a desaturated primary container, deeper night-blue surfaces, and per- theme elevations. New `## Themes` prose covers the "don't invert, reconsider" / saturation-discipline / contrast-target guidelines. Out of scope (follow-ups): - saturation-discipline, density-rule-violation, theme-elevation-mapping rules - DTCG `$extensions['design.md'].themes` themed sets - CLI `--theme` / `--themes-split` flags - spec-gen renderer + spec.mdx Themes section - broken-ref / orphaned-tokens cross-theme awareness - Migration of atmospheric-glass and totality-festival examples Closes #7 — phase 1, 2, 3 (partial), 5 (basic), 7 (one example). https://claude.ai/code/session_01CzQLFXGrZ372zTigJu2uu7
feat(themes): first-class theme primitive (dark / high-contrast / density)
…+copy branch Resolves conflicts with origin/main where the voice+copy primitives (this branch) and the themes / motion / iconography primitives (main) both touched: spec-config.yaml, spec-config.ts, parser & model spec/ handler, dtcg handler, spec-gen sources, and the paws-and-paths example. Resolution approach: keep both feature sets additively. Specifically: - Section order is now Overview → Colors → Typography → Voice → Themes → Layout → Motion → Elevation & Depth → Shapes → Iconography → Components → Do's/Don'ts. - DesignSystemState carries voice/copy *and* themes/activeTheme/ motion/iconography. - DEFAULT_RULE_DESCRIPTORS now totals 38 rules (27 base + 6 voice/copy + 5 motion/icon/theme); types.test.ts and spec.test.ts updated. - DTCG handler emits both the voice/copy `$extensions` and the motion/ iconography token types. - paws-and-paths example carries voice + copy + dark theme overrides. - docs/spec.md regenerated. All 482 tests pass. https://claude.ai/code/session_01Jig9jMBKFgijyTtk1VdFEo
feat(voice+copy): add `voice:` and `copy:` primitives + content linter rules (#8)
Adds a unified Responsive & Layout primitive set so layout becomes a contract instead of a per-screen improvisation: - `breakpoints` (philosophy + values), `grid` (columns + gutter + margin + maxWidth + bleedExceptions), `layoutRules` (contentMaxWidth, stack spacing, formFieldWidth), `templates` (page-level region registry), `pages` (route → template assignments). - Five new linter rules: breakpoint-monotonicity (error), unknown-template (error), missing-region (error), off-grid-dimension (warn), template-region-purity (warn). - Tailwind exporter now emits theme.screens, theme.maxWidth (container, prose), and theme.container.padding from grid.margin. - DTCG exporter emits breakpoints as a sibling dimension group and surfaces grid / layoutRules / templates / pages under $extensions['design.md']. - Layout section in spec.mdx restructured with required prose subsections (mobile-first, breakpoint philosophy, grid usage, readable measure, page templates, region semantics). - paws-and-paths example migrated to declare three templates and populate the new prose subsections; regenerated tokens + tailwind. Source-side scanning (data-template / data-region) is intentionally deferred until issue #6's --src scanner lands. https://claude.ai/code/session_01Ph1qdmfpqFnUUFzEG99CoF
feat(layout): responsive, grid, page templates, layout rules (#9)
Closes the top three friction points surfaced by the Impeccable comparison — all spec/CLI hygiene that needed to land before any DESIGN.md emitter. Item 1: modern color formats - New `model/color.ts` parses hex, rgb()/rgba(), hsl()/hsla(), oklch(), oklab(), lab(), and color(display-p3 …). Wide-gamut sources are converted through linear sRGB so WCAG luminance and contrast checks stay accurate. - ResolvedColor now carries `format` and `raw`; the Tailwind exporter round-trips the original notation so Tailwind v4 can keep oklch / p3 intent. - Validation helpers (isValidColor, isParseableDimension) tolerate non-string inputs so YAML scalars like `opacity: 1` no longer crash the model layer. Item 2: extended component sub-tokens - Added gap, border, outline, opacity, boxShadow, transition, backdropFilter to the allowlist. Renderer now surfaces the per-token description in the generated spec, matching the typography list. - New EXTENDED_COMPONENTS.md fixture exercises every new sub-token alongside oklch / display-p3 / hsl colors and lints clean. Item 3: fixer wired into the CLI - New `design.md fix` command (stdout by default, --write rewrites in place). - `design.md lint --fix` runs section-order auto-fix and writes back. - README and docs/spec.md regenerated.
…ble-style-8npam # Conflicts: # docs/spec.md # packages/cli/src/linter/fixture.test.ts # packages/cli/src/linter/linter/rules/contrast-ratio.test.ts # packages/cli/src/linter/model/handler.ts # packages/cli/src/linter/model/spec.ts # packages/cli/src/linter/spec-config.yaml # packages/cli/src/linter/tailwind/handler.ts
…le-8npam feat: accept modern color formats, expand sub-tokens, wire up fixer
Adds a packages/evals workspace that measures whether DESIGN.md actually helps coding agents produce on-brand UIs, not just whether daily-driving feels good. Methodology: - Loop over (design × task × format × agent). - Format is the A/B: designmd | prose | dtcg | none. - Score each output by color-palette match, font-family match, and spacing-scale snap; aggregate per format. - Two mock agents (token-aware + off-brand) self-test the scorer so the harness runs end-to-end with no API key. The claudeAgent slot is stubbed; wire in @anthropic-ai/sdk to run for real. README documents the methodology, limitations, and the path to a visual-fidelity scorer. https://claude.ai/code/session_01GCBZWo6ghh7YfCpE2eFJFm
…g-nIL7z Sketch eval harness for DESIGN.md fidelity
Tailwind v4 deprecates tailwind.config.js in favor of CSS-first configuration. The export command now emits a `@theme` stylesheet (--color-*, --font-*, --text-*, --radius-*, --spacing-*, --shadow-*, --breakpoint-*, --container-*, --ease-*, --duration-*) instead of a JSON theme.extend object. Examples are regenerated as theme.css. https://claude.ai/code/session_013P12rj4w9pd6FQryAy8YpP
Restructure the CSS exporter to mirror shadcn/ui's globals.css shape: color tokens are declared once in :root with semantic names, per-theme overrides flow through .dark / .high-contrast selectors that swap the same variables, and `@theme inline` aliases each one to Tailwind's --color-* namespace via var(). Non-color tokens (typography, radius, spacing, shadow, breakpoint, motion) emit directly inside @theme. This enables runtime theme switching with a single class toggle without rebuilding the Tailwind theme — flipping `.dark` updates every utility that consumes `--color-*` because the underlying `--*` variables change. https://claude.ai/code/session_013P12rj4w9pd6FQryAy8YpP
…YdUd Migrate Tailwind config to v4 CSS @theme approach
…ntion Pair `on-container` members previously emitted as `on-<pair>` in the Tailwind theme (`--on-primary`, `bg-on-primary`). Rename to `<pair>-foreground` so generated utilities match the shadcn/ui Tailwind v4 convention (`text-primary-foreground`, `--primary-foreground`). The DESIGN.md schema is unchanged — pairs still use `container` / `onContainer` keys, preserving `minContrast` validation and the `mixed-pair-foreground` lint. The rename is applied only at the Tailwind emitter boundary using `pairRole.role === 'on-container'`. https://claude.ai/code/session_01ACjio3fzvcgvZoR7vbnq7g
…ection
- Prepend `@import "tailwindcss";` and `@custom-variant dark (&:is(.dark *));`
so the generated stylesheet is drop-in for shadcn/ui projects.
- Lift `borderRadius` tokens out of `@theme` and into `:root` as
`--rounded-<name>`; alias them in `@theme inline` as
`--radius-<name>: var(--rounded-<name>)`. This mirrors the colors flow
and lets per-theme blocks override radius at runtime via the same
CSS-variable indirection.
Dark theme support is unchanged but worth noting: when a DESIGN.md
declares `themes.dark.colors`, the emitter already renders a `.dark { ... }`
override block (verified against examples/paws-and-paths).
https://claude.ai/code/session_01ACjio3fzvcgvZoR7vbnq7g
…ists Many DESIGN.md files (e.g. examples/paws-and-paths) use Material-style flat naming — `primary` + `on-primary` — rather than declaring a `type: pair` block. Previously the shadcn rename only fired for declared pairs, so those files emitted `--on-primary` instead of `--primary-foreground`. Extend the rename to also fire when an `on-<base>` color has a sibling flat color or ramp named `<base>`. This produces shadcn-shaped output for both pair-declared and M3-flat sources without changing the YAML schema or losing any existing lint coverage. https://claude.ai/code/session_01ACjio3fzvcgvZoR7vbnq7g
…e-azLsy tailwind: rename pair foregrounds to shadcn `<pair>-foreground` convention
The existing harness only measured whether agent-rendered colors and font
families landed in the palette. This adds three more scoring layers that the
README previously called out as missing:
- copy: synthesizes a virtual DesignSystemState from the agent's HTML
(buttons, headings, nav links become components; paragraph text becomes a
prose section) and runs the existing linter copy descriptors —
banned-term, approved-term, button-word-limit, error-pattern, casing,
reserved-name — against it.
- semantic: deterministic per-element assertions on each Task
(e.g. <button> background must resolve to {colors.primary}). The runner
parses inline style, resolves token references, and compares with the
existing color/dimension tolerances.
- structural / vision: linkedom-based selector-presence check (always cheap),
plus an opt-in Playwright screenshot + Claude Sonnet 4.6 vision judge
behind --screenshots / --vision-judge.
Aggregate now drops absent subscores from both numerator and denominator so
disabling a layer doesn't dilute the result. The mock token-aware agent now
looks up named tokens (primary, on-primary, surface, on-surface) so it can
honor semantic assertions in designmd/dtcg formats and visibly fail in
prose/none — preserving the harness self-test.
Also exposes the copy rule descriptors from the public @google/design.md/linter
entry so the eval package can call them via the package alias.
https://claude.ai/code/session_01RrBJ3FJ5KNCkVg4uuQoLLN
evals: add copy, semantic, and vision layers on top of token extraction
This fork isn't published to npm, so `npx @google/design.md` runs the upstream binary without the fork's lint rules and eval changes. Replace all CLI invocations with `bun run cli ...` and document the clone + `bun install` setup. https://claude.ai/code/session_01QZkmJF72BrdVRuxdWXwwW9
…mmand-7I2Gj docs(readme): switch CLI examples to local-clone bun workflow
…KpGbh docs(readme): add fork overview section
Adds unit tests for the highest-correctness-risk modules identified in the coverage audit: - packages/evals/src/score.test.ts: color normalization/distance math, token resolution, dimension scoring, subscore aggregation. - packages/evals/src/semantic.test.ts: DOM-driven CSS assertions including token references, font-family case-insensitivity, minFontSize tolerance, structural element checks. - packages/evals/src/copy.test.ts: virtual component synthesis from HTML and copy-rule scoring against the paws-and-paths fixture. - packages/evals/src/vision.test.ts: judge-response parsing including fenced JSON, clamping, and unparseable input. - packages/cli/src/linter/tailwind/css.test.ts: Tailwind v4 CSS rendering for flat colors, radii, typography metadata, per-theme overrides, and the failed-result throw path. Also exports parseJudgeResponse from vision.ts so it can be unit-tested without mocking the Anthropic SDK. Suite: 577 -> 673 tests, all passing. https://claude.ai/code/session_01DMQPxqRYzUA5xyV7FY4dCh
…-SsR3L test(evals,tailwind): cover scoring engine and CSS renderer
Brings in 37 upstream commits since the April divergence (releases 0.2.0 and 0.3.0): the css-vars export format, the CSS Color Module parser (named colors, hwb, lch, color-mix), nested token declarations with collision detection, the unknown-key / token-like-ignored lint rules, typography sub-property warnings, export/lint exit-code fixes, TTY and ENOENT guards, the Windows designmd alias, and PHILOSOPHY.md. Conflict resolutions of note: - model/handler.ts keeps the fork's pipeline (ramps, pairs, themes, voice, layout) with upstream's improvements grafted in: forEachLeaf nested-token flattening, per-category collision guards, symbol-table driven phase-2 resolution (extended to elevation), unknown-key tracking, and defensive dimension parsing. - Color parsing accepts the union of both engines: the fork's parser (incl. display-p3) first, upstream's parseCssColor as fallback for named colors, hwb(), lch(), and color-mix(); ColorFormat gains 'css'. - export keeps the fork's shadcn-style Tailwind v4 CSS emitter under css-tailwind, with `tailwind` aliasing it (not json-tailwind as upstream chose); upstream's v3 JSON emitter lives at json-tailwind and upstream's TailwindV4EmitterHandler remains a library export. - orphaned-tokens combines the fork's ramp/pair provenance exemptions with upstream's MD3 family heuristics. - SCHEMA_KEYS extended with the fork's schema (themes, voice, copy, motion, iconography, layout, registry) so unknown-key stays quiet. - Upstream tests asserting raw number/boolean component scalars were adapted to the fork's coerce-to-string contract; 'red' is now a valid color, so validator tests assert acceptance instead of rejection. All 774 workspace tests pass; docs/spec.md regenerated via spec:gen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Author
|
Opened against the wrong repo (this was meant for the fork). Apologies for the noise. |
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Merges
google-labs-code/design.md@main(releases 0.2.0 + 0.3.0, 37 commits) into the fork, resolving 18 conflicted files. Full analysis: the branches diverged 2026-04-22; upstream added two export formats, a spec-grade color engine, five lint rules, and a long tail of CLI hardening.New from upstream
--format css-varsexport; upstream's Tailwind v4 emitter kept as a library exporthwb(),lch(),color-mix()(fallback behind the fork's parser, which keepsdisplay-p3andformat/rawround-trip metadata)unknown-key(Levenshtein "did you mean") andtoken-like-ignoredlint rules; typography sub-property warningsdesignmdalias, bounded validation costKey resolutions (details in the merge commit and CHANGELOG.md)
tailwindexport stays aliased to the fork's shadcn-stylecss-tailwindCSS (deviates from upstream'sjson-tailwindalias to avoid silently changing fork output); v3 JSON available atjson-tailwindorphaned-tokenscombines fork ramp/pair provenance with upstream MD3 family heuristicsSCHEMA_KEYSextended with fork primitives sounknown-keystays quiet on themTest plan
bun run lintcleanbun run test— 774 pass / 0 fail across cli + evalscolor-mix; typo'd top-level key gets a suggestion🤖 Generated with Claude Code